package com.android.filter;

import java.io.OutputStream;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.Bitmap.CompressFormat;

public class PreviewImage {
	public PreviewImage(Context context, int width, int height)
	{
		mPreviewMatrix = new Matrix();
		mPinchZoomMatrix = new Matrix();
		mDrawMatrix = new Matrix();
		mPreviewWidth = width;
		mPreviewHeight = height;
		mDrawPath = new Path();
		mTextPaint = new Paint();
		mTextPaint.setColor(Color.BLACK);
		mTextPaint.setTextSize(20.f);
	}
	public void init(int viewWidth, int viewHeight)
	{
		float viewRatio = viewWidth / (float)viewHeight;
		float previewRatio = mPreviewWidth / (float)mPreviewHeight;
		float left = 0.f, top = 0.f;
		float scale = 1.f;
		float resizeWidth = 0.f;
		float resizeHeight = 0.f;
		RectF prevInitRoi = new RectF();
		Matrix prevPreviewMatrix = new Matrix();
		PointF imageFocus = null;
		
		if(viewRatio > previewRatio)
		{
			scale = viewHeight / (float)mPreviewHeight;
			resizeWidth = mPreviewWidth * scale;
			resizeHeight = mPreviewHeight * scale;
			left = (viewWidth - resizeWidth) * .5f;
			top = (viewHeight - resizeHeight) * .5f;
		}
		else
		{
			scale = viewWidth / (float)mPreviewWidth;
			resizeWidth = mPreviewWidth * scale;
			resizeHeight = mPreviewHeight * scale;
			left = (viewWidth - resizeWidth) * .5f;
			top = (viewHeight - resizeHeight) * .5f;
		}
		
		if(mInitDrawRoi == null)
		{
			mInitDrawRoi = new RectF();
			prevInitRoi.set(left, top, left + resizeWidth, top + resizeHeight);
			
			prevPreviewMatrix.reset();
			prevPreviewMatrix.postScale(scale, scale);
			prevPreviewMatrix.postTranslate(left, top);
			imageFocus = getImageFocus(prevInitRoi, mDrawMatrix, prevPreviewMatrix);
		}
		else
		{
			prevInitRoi.set(mInitDrawRoi);
			prevPreviewMatrix.set(mPreviewMatrix);
			imageFocus = getImageFocus(prevInitRoi, mDrawMatrix, prevPreviewMatrix);
		}
		mInitDrawRoi.set(left, top, left + resizeWidth, top + resizeHeight);
		
		mPreviewMatrix.reset();
		mPreviewMatrix.postScale(scale, scale);
		mPreviewMatrix.postTranslate(left, top);
		
		refreshPinchZoom(imageFocus);
		update(viewWidth, viewHeight);
	}
	
	public void resetPinchZoom(int viewWidth, int viewHeight)
	{
		float pinchZoomScale = 1.f;
		float[] values = new float[9];
		mPinchZoomMatrix.getValues(values);
		pinchZoomScale = values[Matrix.MSCALE_X];
		
		pinchZoomScale = 1.f / values[Matrix.MSCALE_X];
		
		mPinchZoomMatrix.postScale(pinchZoomScale, pinchZoomScale, viewWidth/2, viewHeight/2);
		update(viewWidth, viewHeight);
		
		values = null;
	}
	public void setPinchZoomScale(float scale, float focusX, float focusY, int viewWidth, int viewHeight)
	{
		float pinchZoomScale = 1.f;
		float[] values = new float[9];
		mPinchZoomMatrix.getValues(values);
		pinchZoomScale = values[Matrix.MSCALE_X] * scale;
		
		if(pinchZoomScale > 1.8f)
			scale = 1.8f / values[Matrix.MSCALE_X];
		if(pinchZoomScale < 1.f)
			scale = 1.f / values[Matrix.MSCALE_X];
		
		mPinchZoomMatrix.postScale(scale, scale, focusX, focusY);
		update(viewWidth, viewHeight);
		
		values = null;
	}
	
	public boolean isZoomed()
	{
		boolean ret = false;
		float[] values = new float[9];
		mPinchZoomMatrix.getValues(values);
		if(values[Matrix.MSCALE_X] > 1.f)
			ret = true;
		return ret;
	}
	public void setTranslateX(float diffX, float diffY, int viewWidth, int viewHeight)
	{
		mPinchZoomMatrix.postTranslate(diffX, diffY);
		update(viewWidth, viewHeight);
	}
	public void destroy()
	{
		Util.recycleBitmap(mPreview);
	}
	public void drawPreview(Canvas canvas, Paint paint)
	{
		mDrawMatrix.set(mPreviewMatrix);
		mDrawMatrix.postConcat(mPinchZoomMatrix);
		if(mDrawBitmap)
		{
			if(mPreview != null && !mPreview.isRecycled())
			{
				canvas.drawBitmap(mPreview, mDrawMatrix, paint);
			}		
		}
		else
		{
			if(mDrawPath != null && mPathPaint != null)
			{
				canvas.save();
				RectF dst = new RectF(0, 0, mPreviewWidth, mPreviewHeight);
				mDrawMatrix.mapRect(dst);
				canvas.clipRect(dst);
				if(mPath != null)
					mPath.transform(mDrawMatrix, mDrawPath);
				canvas.drawPath(mDrawPath, mPathPaint);
				canvas.restore();
			}
		}
	}
	public void drawBitmap(Canvas canvas, Paint paint)
	{
//		mDrawMatrix.set(mPreviewMatrix);
//		mDrawMatrix.postConcat(mPinchZoomMatrix);
			if(mPreview != null && !mPreview.isRecycled())
			{
				canvas.drawBitmap(mPreview, 0, 0, paint);
			}		
		
	}
	public void setBitmap(Bitmap bitmap)
	{
		mDrawBitmap = true;
		mPreview = bitmap;
		mPreviewWidth = bitmap.getWidth();
		mPreviewHeight = bitmap.getHeight();
	}
	public void setPath(Path path, Paint paint)
	{
		mDrawBitmap = false;
		mPath = path;
		mPathPaint = paint;
	}
	public int getWidth()
	{
		return mPreviewWidth;
	}
	public int getHeight()
	{
		return mPreviewHeight;
	}
	public Matrix getPreviewMatrix()
	{
		return new Matrix(mDrawMatrix);
	}
	public void saveBitmap(OutputStream os)
	{
		mPreview.compress(CompressFormat.PNG, 100, os);
	}
	private void update(int viewWidth, int viewHeight)
	{
		float dx = 0.f, dy = 0.f;
		RectF dst = new RectF();
		mPinchZoomMatrix.mapRect(dst, mInitDrawRoi);
		
		if(dst.width() > viewWidth)
		{
			if(dst.left > 0)
				dx = -dst.left;
			if(dst.right < viewWidth)
				dx = viewWidth - dst.right;
		}
		else
		{
			float tempLeft = (viewWidth - dst.width()) * .5f;
			dx = -dst.left + tempLeft;
		}
		if(dst.height() > viewHeight)
		{
			if(dst.top > 0)
				dy = -dst.top;
			if(dst.bottom < viewHeight)
				dy = viewHeight - dst.bottom;
		}
		else
		{
			float tempTop = (viewHeight - dst.height()) * .5f;
			dy = -dst.top + tempTop;
		}
		mPinchZoomMatrix.postTranslate(dx, dy);
	}
	private void refreshPinchZoom(PointF imageFocus)
	{
		float[] values = new float[9];
		float[] pts = new float[2];
		float scale;
		pts[0] = imageFocus.x;
		pts[1] = imageFocus.y;
		mPinchZoomMatrix.getValues(values);
		scale = values[Matrix.MSCALE_X];
		mPinchZoomMatrix.reset();
		mPinchZoomMatrix.postScale(scale, scale);
		
		mDrawMatrix.set(mPreviewMatrix);
		mDrawMatrix.postConcat(mPinchZoomMatrix);
		
		mDrawMatrix.mapPoints(pts);
		
		mPinchZoomMatrix.postTranslate(mInitDrawRoi.centerX() - pts[0], mInitDrawRoi.centerY() - pts[1]);
	}
	private PointF getImageFocus(RectF prevInitRoi, Matrix drawMatrix, Matrix prevMatrix)
	{
		Matrix im = new Matrix();
		RectF dst = new RectF();
		float[] center = new float[2];
		drawMatrix.set(prevMatrix);
		drawMatrix.postConcat(mPinchZoomMatrix);
		mPinchZoomMatrix.mapRect(dst, prevInitRoi);
		center[0] = prevInitRoi.centerX();
		center[1] = prevInitRoi.centerY();
		drawMatrix.invert(im);
			
		im.mapPoints(center);
		
		return new PointF(center[0], center[1]);
	}
	private boolean mDrawBitmap = false;
	private int mPreviewWidth = 0;
	private int mPreviewHeight = 0;
	private RectF mInitDrawRoi = null;
	private Paint mPathPaint = null;
	private Paint mTextPaint = null;
	private Path mDrawPath = null;
	private Path mPath = null;
	private Matrix mPreviewMatrix = null;
	private Matrix mPinchZoomMatrix = null;
	private Matrix mDrawMatrix = null;
	private Bitmap mPreview = null;
}
